//
// Created by xu on 2024/12/4.
//
#include <stdio.h>
#include <libavutil/imgutils.h>
#include <libavutil/opt.h>
#include <libswscale/swscale.h>

#define ERR_BUF \
    char errbuf[1024]; \
    av_strerror(ret, errbuf, sizeof (errbuf));

#define END(func) \
    if (ret < 0) { \
        ERR_BUF; \
        av_log(NULL, AV_LOG_INFO, #func" error %s\n", errbuf); \
        goto end; \
    }


typedef struct {
    const char *filename;
    int width;
    int height;
    enum AVPixelFormat format;
} RawVideoFile;

void convert_raw_video(RawVideoFile *in, RawVideoFile *out) {
    // yuv420p -> yuv444p
    // 上下文
    SwsContext *ctx = NULL;
    // 输入、输出缓冲区（指向每一个平面的数据）
    uint8_t *inData[4], *outData[4];
    // 每一个平面一行的大小
    int inStrides[4], outStrides[4];
    // 每一帧图片的大小
    int inFrameSize, outFrameSize;
    int ret = 0;
    // 进行到了哪一帧
    int frameIdx = 0;

    FILE *inFile = NULL, *outFile = NULL;
    // 创建上下文
    ctx = sws_getContext(in->width, in->height, in->format,
                         out->width, out->height, out->format,
                         SWS_BILINEAR, NULL,NULL, NULL);
    if (!ctx) {
        av_log(NULL, AV_LOG_INFO, "sws_getContext error\n");
        goto end;
    }

    // 输入缓冲区
    ret = av_image_alloc(inData, inStrides, in->width, in->height, in->format, 1);
    END(av_image_alloc);

    printf(" ====> %d\n", ret);

    // 输出缓冲区
    ret = av_image_alloc(outData, outStrides, out->width, out->height, out->format, 1);
    END(av_image_alloc);

    printf(" ====> %d\n", ret);

    // 打开文件
    inFile = fopen(in->filename, "rb");
    if (inFile == NULL) {
        av_log(NULL, AV_LOG_INFO, "File open error %s\n", in->filename);
        goto end;
    }
    outFile = fopen(out->filename, "wb");
    if (outFile == NULL) {
        av_log(NULL, AV_LOG_INFO, "File open error %s\n", out->filename);
        goto end;
    }

    // 计算每一帧图片的大小
    inFrameSize = av_image_get_buffer_size(in->format, in->width, in->height, 1);
    outFrameSize = av_image_get_buffer_size(out->format, out->width, out->height, 1);

    // 进行每一帧的转换
    while (fread(inData[0], 1, inFrameSize, inFile) == inFrameSize) {
        sws_scale(ctx, (const uint8_t *const *) inData, inStrides, 0, in->height,
                  outData, outStrides);
        // 写到输出文件
        fwrite(outData[0], 1, outFrameSize, outFile);
        av_log(NULL, AV_LOG_INFO, "转换完第 %d 帧\n", frameIdx++);
    }

    end:
    fclose(inFile);
    fclose(outFile);
    av_freep(&inData[0]);
    av_freep(&outData[0]);
    sws_freeContext(ctx);
}

int main(int argc, char *argv[]) {
    RawVideoFile in = {
            .filename="C:/Users/xu/Desktop/out.yuv",
            .width=1920,
            .height=1080,
            .format=AV_PIX_FMT_YUV420P
    };

    RawVideoFile out = {
            .filename="C:/Users/xu/Desktop/out_yuv444p.yuv",
            .width=1920,
            .height=1080,
            .format=AV_PIX_FMT_YUV444P
    };
    convert_raw_video(&in, &out);
    return 0;
}